/*
 *Copyright (c) [2019] [name of copyright holder]
 *[Software Name] is licensed under the Mulan PSL v1.
 *You can use this software according to the terms and conditions of the Mulan PSL v1.
 *You may obtain a copy of Mulan PSL v1 at:
 *http://license.coscl.org.cn/MulanPSL
 *THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 *IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 *PURPOSE.
 *See the Mulan PSL v1 for more details.
 */

/*!
 * \autor captainpeng
 * \date 2019-12-20
 * \update 2019-12-20
 * \version 1.0
 * \copyright
 */

#ifndef MY_UNICODE_STRING_HPP
#define MY_UNICODE_STRING_HPP

#include"mystd.hpp"
#include"unicode.hpp"
#include"utf8.hpp"

namespace my{

    class ustring{
    public:
        using charType = unicode_t;
        using chLib = charLib<charType>;
		using buffType = my::buffer<charType>;
		using charPtr = typename buffType::elementPtr;
        using iteratorType = typename buffType::iteratorType;
        using iteratorConstType = typename buffType::iteratorConstType;

	private:
		buffType mStr;
		charPtr mCur;

        // 针对不同的 c 字符串, 记得先分配内存
        static inline size_t Copy(const char * input, size_t size, charPtr begin, charPtr end){
            // utf8
            int step = 0;
			charPtr forward = begin;
            for(size_t i = 0; i < size && forward < end; i += step, ++forward){
                *forward = utf8::CStrToUnicode(input + i, step);
            }

            *forward = unicode::empty;
			return forward - begin;
        }
        
        static inline size_t Copy(const wchar_t * input, size_t size, charPtr begin, charPtr end){
			charPtr forward = begin;
            for(size_t i = 0; i < size && forward < end; ++i, ++forward)
                *forward = *(input + i);
            
            *forward = unicode::empty;
			return forward - begin;
        }

        static inline size_t Copy(const unicode_t * input, size_t size, charPtr begin, charPtr end){
			charPtr forward = begin;
            for(size_t i = 0; i < size && forward < end; ++i, ++forward)
                *forward = *(input + i);
            
            *forward = unicode::empty;
			return forward - begin;
        }

        template<typename CharT>
        static inline size_t AllocAndCopy(const CharT * str, size_t size, buffType & buff){
            if(str == nullptr || size == 0){
                *buff = unicode::empty;
                return 0;
            }

            buff.realloc(size + 1);
            return Copy(str, size, buff.begin(), buff.end()-1);
        }

		template<typename CharT>
		static inline size_t AllocAndCopy(const CharT * str, buffType & buff){
			size_t csize = (str == nullptr)? 1:charLib<CharT>::strlen(str) + 1;
			if(csize == 1){
				*buff = unicode::empty;
				return 0;
			}

			buff.realloc(csize);
            return Copy(str, csize-1, buff.begin(), buff.end()-1);
		}

		template<typename CharT>
		static inline size_t AllocAndCopy(const std::basic_string<CharT> & str, buffType & buff){
			size_t csize = str.size()+1;
			if(csize == 1){
				*buff = unicode::empty;
				return 0;
			}

			buff.realloc(csize);
            return Copy(str.c_str(), str.size(), buff.begin(), buff.end()-1);
		}

        // 因为要多出一位存 0 
		void Resize(buffType & buff, size_t len){
            size_t size = (size_t)buff.size();
            ++len;
			size = len > size? len/8*8+0x11:len;
			buff.resize(size);
		}

    public:
        template<typename CharT>
        ustring(const CharT * cstr):
			mStr()
            {
                size_t i = AllocAndCopy(cstr, mStr);
                mCur = i + mStr.begin();
            }

        template<typename CharT>
        ustring(const std::basic_string<CharT> & str):
			mStr()
            {
                size_t i = AllocAndCopy(str, mStr);
                mCur = i + mStr.begin();
            }

        template<typename CharT>
        ustring & operator=(const CharT * cstr){
            size_t i = AllocAndCopy(cstr, mStr);
            mCur = i + mStr.begin();
            return *this;
        }

        template<typename CharT>
        ustring & operator=(const std::basic_string<CharT> & str){
            size_t i = AllocAndCopy(str, mStr);
			mCur = i + mStr.begin();
            return *this;
        }

        // 区域
        template<typename CharT>
        ustring(const CharT * cstr, size_t size):mStr(){
            size_t i = AllocAndCopy(cstr, size, mStr);
            mCur = i + mStr.begin();
        }

        ustring(const char c):
            mStr(2),
            mCur(mStr.end()-1)
            {
                mStr[0] = c;
                mStr[1] = unicode::empty;
            }

        ustring(const wchar_t c):
            mStr(2),
            mCur(mStr.end()-1)
            {
                mStr[0] = c;
                mStr[1] = unicode::empty;
            }

        ustring(const my::unicode_t c):
            mStr(2),
            mCur(mStr.end()-1)
            {
                mStr[0] = c;
                mStr[1] = unicode::empty;
            }

        // 默认构造函数, 复制构造函数, 移动构造函数
        ustring():
			mStr(),
			mCur(mStr.begin())
            {
                *mCur = unicode::empty;
            }

        ustring(size_t size):
            mStr(size),
            mCur(mStr.begin())
            {
                *mCur = unicode::empty;
            }

        ustring(const ustring & ustr):
			mStr(ustr.mStr),
			mCur(mStr.begin() + ustr.size())
            {}

        ustring(ustring && ustr):
			mStr(std::move(ustr.mStr)),
			mCur(ustr.mCur)
            {
                ustr.mCur = ustr.mStr.begin();
                *(ustr.mCur) = unicode::empty;
            }

        ustring & operator=(const ustring & rhs){
            if(this == &rhs) return *this;

            int i = AllocAndCopy(rhs.c_str(), mStr);
            mCur = i + mStr.begin();
            return *this;
        }

        ustring & operator=(ustring && rhs){
            if(this == &rhs) return *this;
			
            mStr.swap(rhs.mStr);
			std::swap(mCur, rhs.mCur);
            return *this;
        }

        ~ustring(){}
        
        // 一些常用接口
        template<typename CharT>
        ustring & set(const CharT * cstr, size_t size){
            AllocAndCopy(cstr, size, mStr);
            return *this;
        }

        void reset(){
			*mStr = unicode::empty;
			mCur = mStr.begin();
        }
        
        const inline size_t size() const {
            return mCur - mStr.begin();
        }

		const inline size_t capacity() const {
			return mStr.size();
		}
		
        const inline unicode_t * data() const {
            return mStr.begin();
        }

		const inline unicode_t * c_str() const {
			return mStr.begin();
		}

        // 预留内存记得多保留一位放置 unicode::empty
        void keep(const size_t size){
            mStr.resize(size);
        }

        iteratorType begin(){
            return mStr.begin();
        }

        iteratorConstType begin() const {
            return mStr.begin();
        }

        iteratorType end(){
            return mCur;
        }

        iteratorConstType end() const {
            return mCur;
        }

        inline void clear(){
            mCur = mStr.begin();
        }

        const charType & rat(int index){
            return mStr[index];
        }

        charType & lat(int index){
            return mStr[index];
        }

        charType & operator[](int index){
            return mStr[index];
        }

        const charType & operator[](int index) const {
            return mStr[index];
        }

        void swap(ustring & rhs){
            mStr.swap(rhs.mStr);
            std::swap(mCur, rhs.mCur);
        }

		template<typename CharT>
		ustring & append(const CharT * cstr){
			size_t oldsize = size();
            size_t csize = charLib<CharT>::strlen(cstr);
			Resize(mStr, oldsize + csize);
            int newsize = oldsize + Copy(cstr, csize, mStr.begin()+oldsize, mStr.end()-1);
            mCur = newsize + mStr.begin();
			return *this;
		}

		template<typename CharT>
		ustring & append(const std::basic_string<CharT> & str){
			size_t oldsize = size();
			Resize(mStr, oldsize + str.size());
            int newsize = oldsize + Copy(str.c_str(), str.size(),  mStr.begin()+oldsize, mStr.end()-1);
            mCur = newsize + mStr.begin();
			return *this;
		}

		ustring & append(const ustring & ustr){
			size_t oldsize = size();
			Resize(mStr, oldsize + ustr.size());
            int newsize = oldsize + Copy(ustr.c_str(), ustr.size(), mStr.begin()+oldsize, mStr.end()-1);
			mCur = newsize + mStr.begin();
			return *this;
		}

		std::string toString() const {
			std::string str;
			const unicode_t * forward = mStr.begin();
			for(; *forward != unicode::empty; ++forward){
				utf8::StrAddUnicode(str, *forward);
			}

			return str;
		}

        template<typename CALL, typename ... ARGS>
        void each(const CALL & call, ARGS && ... args){
            for(auto it = begin(); it != end(); ++it)
                if(!call((*it), std::forward<ARGS>(args) ... ))
                    return ;
        }

        static int comp(const ustring & lhs, const ustring & rhs){
            const unicode_t * u1 = lhs.data();
            const unicode_t * u2 = rhs.data();

            int result = 0;

            for(;*u1 != unicode::empty && *u2 != unicode::empty; ++u1, ++u2)
                if(*u1 != *u2)
                    break;

            if(*u1 > *u2)
                result = 1;
            else if(*u1 < *u2)
                result = -1;
            else
                result = 0;

            return result;
        }

	private:
		void resize(int length){
			mStr.resize(size() + length + 32); 
		}
    };

	bool operator==(const ustring & lhs, const ustring & rhs){
        return (lhs.size() == rhs.size()) && (ustring::comp(lhs, rhs)) == 0;
	}

	bool operator!=(const ustring & lhs, const ustring & rhs){
		return !(lhs == rhs);
	}

	bool operator>(const ustring & lhs, const ustring & rhs){
        return ustring::comp(lhs, rhs) > 0;
	}

	bool operator<(const ustring & lhs, const ustring & rhs){
        return ustring::comp(lhs, rhs) < 0;
	}

    bool operator>=(const ustring & lhs, const ustring & rhs){
        return ustring::comp(lhs, rhs) >= 0;
    }

    bool operator<=(const ustring & lhs, const ustring & rhs){
        return ustring::comp(lhs, rhs) <= 0;
    }

    ustring operator+(const ustring & lhs, const ustring & rhs){
        ustring s;
        s.keep(lhs.size() + rhs.size() + 1);
        s.append(lhs);
        s.append(rhs);
        return s;
    }
    
    std::ostream & operator<<(std::ostream & os, const ustring & str){
        os<<str.toString();
        return os;
    }
}

#endif
